package zdream.pmfield.stage.effect;

import zdream.pmfield.data.foe.BStat;
import zdream.pmfield.data.foe.MoveAdditionalEffects;
import zdream.pmfield.stage.director.HookStore;
import zdream.pmfield.stage.director.HookType;
import zdream.pmfield.stage.director.Hooks;
import zdream.pmfield.stage.object.FoeActor;
import zdream.pmfield.stage.object.FoeActor.BStatLevelUpdateReport;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

/**
 * @author Zdream
 * @date 2023-12-12
 * @since 0.0.1
 */
public class BStatChangeEffect extends AbstractEffect {
	private static final String DESC_KEY_CHANGE = MoveAdditionalEffects.K_BSTAT_CHANGE_CHANGE;
	private static final String DESC_KEY_TARGET = MoveAdditionalEffects.K_BSTAT_CHANGE_TARGET;
	private static final byte INVALID_VALUE = Byte.MIN_VALUE;

	public final boolean change;
	public final EffectTarget target;
	public final byte[] bStats = new byte[BStat.values().length];

	public BStatChangeEffect(MoveAdditionalEffects.EffectDescription description) {
		super(description.category);

		final Map<String, ?> parsed = MoveAdditionalEffects.parse(description);
		Objects.requireNonNull(parsed);
		change = (Boolean) parsed.get(DESC_KEY_CHANGE);
		target = EffectTarget.valueOf((String) parsed.get(DESC_KEY_TARGET));

		Arrays.fill(bStats, INVALID_VALUE);

		BStat[] values = BStat.values();
		for (int i = 1; i < values.length; i++) {
			BStat bStat = values[i];
			Integer value = (Integer) parsed.get(bStat.name());
			if (value != null) {
				bStats[i] = value.byteValue();
			}
		}
	}

	@Override
	public void init(Function<HookType, HookStore.HookHandlerBuilder> onHook) {
		onHook.apply(HookType.AFTER_EXECUTE_ATTACK)
				.handle(this::afterAttackExecuting)
				.buildAndSave();
		onHook.apply(HookType.ON_CREATE_BUFF_BY_ABILITY)
				.handle(this::createBuffByAbility)
				.buildAndSave();
	}

	private void afterAttackExecuting(Hooks.HookContext rawContext) {
		final Hooks.AttackHookContext context = rawContext.as();
		final List<FoeActor> foes = affectedFoesWithTarget(context.attackContext, target);
		foes.forEach(this::commitLevelUpdateReportForFoe);
	}

	private void createBuffByAbility(Hooks.HookContext rawContext) {
		final Hooks.FoeBuffContext context = rawContext.as();

		if (target != EffectTarget.self) {
			throw new IllegalArgumentException("从 ability 触发的必须只能修改自己的能力等级");
		}

		commitLevelUpdateReportForFoe(context.foe);
	}

	private void commitLevelUpdateReportForFoe(FoeActor foe) {
		final BStatLevelUpdateReport report = foe.updateBStatLevel();
		for (int i = 0; i < bStats.length; i++) {
			final byte value = bStats[i];
			if (value == INVALID_VALUE) {
				continue;
			}

			if (change) {
				report.withBStatChange(BStat.values()[i], value);
			} else {
				report.withBStatSet(BStat.values()[i], value);
			}
		}
		report.withTitle(category).commit();
	}
}
